﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;

namespace Server
{
    /// <summary>
    /// This class maintains the connection and stream information for a particular user.
    /// </summary>
    public class ClientHandler
    {
        /// <summary>
        /// The socket for this connection
        /// </summary>
        private Socket sock;

        /// <summary>
        /// The input stream opened on the socket
        /// </summary>
        private StreamReader input;

        /// <summary>
        /// The output stream opened on the socket
        /// </summary>
        private StreamWriter output;

        /// <summary>
        /// The user associated with this connection
        /// </summary>
        private User myUser;

        /// <summary>
        /// The client thread to listen for server responses
        /// </summary>
        private Thread listenerThread;

        /// <summary>
        /// Creates a new instance of the class, opens the streams on the socket
        /// </summary>
        /// <param name="s">The socket for this connection</param>
        public ClientHandler(Socket s, UserManager um)
        {
            this.sock = s;
            // DONE: Create the input/output streams for the given socket.
            NetworkStream ns = new NetworkStream(s);
            input = new StreamReader(ns);
            output = new StreamWriter(ns);

            listenerThread = new Thread(new ParameterizedThreadStart(HandleClient));
            listenerThread.Start(um);
        }

        /// <summary>
        /// Sends a response out on the socket output stream.
        /// </summary>
        /// <param name="data">The string message to send out</param>
        public void SendResponse(string data)
        {
            // DONE: Put code here to write the data out to the socket output stream
            output.WriteLine(data);
            output.Flush();

            Console.WriteLine("Wrote response " + data);
        }

        /// <summary>
        /// This method reads a line of input coming in off the socket.
        /// </summary>
        /// <returns>the line of text read</returns>
        public string GetResponse()
        {
            // DONE: put code here to read a line of input from input stream
            try
            {
                string res = input.ReadLine();
                Console.WriteLine("Got message ", res);
                return res;
            }
            catch (System.NullReferenceException)
            {
                return null;
            }
            catch (System.ObjectDisposedException)
            {
                return null;
            }
        }

        /// <summary>
        /// This method closes the connection by closing the streams
        /// </summary>
        public void Shutdown()
        {
            Console.WriteLine("Disconnected from client: " + sock.Handle);
            input.Close();
            input = null;
            output.Close();
            output = null;
        }

        /// <summary>
        /// This method makes up our thread for this socket.  We loop continually getting the input and
        /// deciding what to do with it.  Input text is parsed by using tab separators
        /// </summary>
        /// <param name="um">The user manager for the server</param>
        public void HandleClient(object o)
        {
            UserManager um = (UserManager)o;
            bool running = true;
            string message;
            // Loop Continually until client wants to disconnect
            while (running)
            {
                // Get the input from the user
                string input = this.GetResponse();
                // TODO: Fill out the logic here to break input up into its parts and handle the message correctly
                // need to break out of this loop if the BYE message is received
                if (input == null)
                {
                    running = false;
                    break;
                }

                string[] tokens = input.Split("\t".ToCharArray());
                message = "";
                switch (tokens[0])
                {
                    case "REGISTER":
                        if (um.AddUser(tokens[1], tokens[2], this, out myUser))
                        {
                            // registration succeeded
                            message = "OK";
                        }
                        else
                        {
                            // duplicate username
                            message = "FAIL";
                        }
                        break;
                    case "LOGIN":
                        myUser = um.FindUser(tokens[1]);
                        if (myUser == null)
                        {
                            message = "UNKNOWN";
                        }
                        else if (!myUser.Authenticate(tokens[2], this))
                        {
                            message = "BADPWD";
                        }
                        else
                        {
                            message = "OK";
                            myUser.IsLoggedIn = true;
                        }
                        break;
                    case "CHAT":
                        if (myUser != null && myUser.IsLoggedIn)
                        {
                            // if user object exists and is logged in send the message
                            um.SendToAll(tokens[1], myUser);
                        }
                        else
                        {
                            // otherwise report an error
                            message = "ERROR";
                        }
                        break;
                    case "BYE":
                        running = false;
                        //When client has signalled to shutdown, then shutdown the connection
                        Shutdown();
                        return;
                    default:
                        break;
                }

                // DONE: Send the correct response to the client
                if (message.Length > 0)
                {
                    this.SendResponse(message);
                }
            }
        }
    }
}
